import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * <code>MarkovStateMgr</code> is a singleton class that is used to ensure
 * that no duplicate <code>MarkovState</code>s are created.
 */
public class MarkovStateMgr
{
	private static MarkovStateMgr singleton = null;
	
	/**
	 * Get the singleton <code>MarkovStateMgr</code>.
	 * 
	 * @param cons <code>Constructor</code> used to create 
	 * 		<code>MarkovState</code>s when they don't exist. Must extend the
	 * 		abstract class <code>MarkovState</code>
	 * @return
	 */
	public static MarkovStateMgr getSingleton()
	{
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("ENTER: MarkovStateMgr.getSingleton ()");
		
		if(singleton == null)
			singleton = new MarkovStateMgr();
		

		if(MarkovModel.VERBOSITY == 3)
			System.out.println("EXIT: MarkovStateMgr.getMarkovStateSet ("+singleton+")");
		return singleton;
	}
	
	private Map<Object, MarkovState> stateIndex = 
		new HashMap<Object,MarkovState>();
	private Map<Object[], MarkovStateSet> stateSetIndex = 
		new HashMap<Object[],MarkovStateSet>();
	
	private MarkovStateMgr()
	{
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("ENTER: MarkovStateMgr.MarkovStateMgr ()");
		
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("EXIT: MarkovStateMgr.MarkovStateMgr ()");
	}
	
	/*
	public MarkovStateSet getMarkovStateSet(MarkovStateSet states)
	{
		return getMarkovStateSet((MarkovState[])states.getStates().toArray());
	}*/
	public MarkovStateSet getMarkovStateSet(MarkovStateSet states)
	{
		List<Object> tokenList = new LinkedList<Object>();
		for(MarkovState ms : states.getStates())
		{
			tokenList.add(ms.getToken());
		}
		return getMarkovStateSet(tokenList.toArray());
	}
	/**
	 * Get the <code>MarkovStateSet</code> for the input tokens. This function
	 * is used to ensure that there are no redundant copies of 
	 * <code>MarkovStateSet</code> instances.
	 * 
	 * @param tokens <code>Object[]</code>
	 * @return <code>MarkovStateSet</code>
	 */
	public MarkovStateSet getMarkovStateSet(Object[] tokens)
	{
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("ENTER: MarkovStateMgr.getMarkovStateSet ("+tokens+")");
		
		MarkovStateSet result = stateSetIndex.get(tokens);
		if(result == null)
		{
			List<MarkovState> markovStates = new LinkedList<MarkovState>();
			for(Object token : tokens)
			{
				markovStates.add(getMarkovState(token));
			}
			result = new MarkovStateSet(markovStates);
			stateSetIndex.put(tokens, result);
		}
		
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("EXIT: MarkovStateMgr.getMarkovStateSet ("+result+")");
		return result;
	}
	
	/**
	 * Get the <code>MarkovState</code> for the input token. This function
	 * is used to ensure that there are no redundant copies of 
	 * <code>MarkovState</code> instances.
	 * 
	 * @param token <code>Object</code>
	 * @return <code>MarkovState</code>
	 */
	public MarkovState getMarkovState(Object token)
	{
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("ENTER: MarkovStateMgr.getMarkovState ("+token+")");
		
		MarkovState result = stateIndex.get(token);
		if(result == null)
		{
			result = new MarkovState(token);
			stateIndex.put(token, result);
		}

		if(MarkovModel.VERBOSITY == 3)
			System.out.println("EXIT: MarkovStateMgr.getMarkovState ("+result+")");
		return result;
	}
	
	protected Map<Object,MarkovState> getStates()
	{
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("ENTER: MarkovStateMgr.getStates ()");

		if(MarkovModel.VERBOSITY == 3)
			System.out.println("EXIT: MarkovStateMgr.getStates ("+stateIndex+")");
		return stateIndex;
	}
	
	protected Map<Object[], MarkovStateSet> getStateSet()
	{
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("ENTER: MarkovStateMgr.getStateSets ()");
		
		if(MarkovModel.VERBOSITY == 3)
			System.out.println("EXIT: MarkovStateMgr.getStateSets ("+stateSetIndex+")");
		return stateSetIndex;
	}
}
